#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <sstream>

using namespace std;

bool patchFile(const string& fileNameIn, const string& fileNameOut, unsigned char* libraryKey, unsigned char* replacementId)
{
	unsigned int offset = 0;
	ifstream ifs(fileNameIn.c_str());
	ofstream ofs(fileNameOut.c_str());
	
	unsigned char buffer[9];
	
	bool found = false;
	
	while ( !ifs.eof() )
	{
		memset(buffer, 0, 9);
		ifs.read((char*)buffer, 8);
		bool stillOK = true;
		for ( unsigned i = 0; i < 8; ++i )
		{
			if (  buffer[i] != libraryKey[i] )
			{
				stillOK = false;
				continue;
			}
		}
		
		if ( stillOK )
		{
			ofs.write((const char*)replacementId, 8);
			found = true;
			break;
		}
		else
		{
			ofs.write((const char*)buffer, 1);
			offset++;
			ifs.seekg(offset, ios_base::beg);
		}
	}
	
	if ( found )
	{
		unsigned int pos = ifs.tellg();
		ifs.seekg(0, ios_base::end);
		unsigned size = (unsigned)ifs.tellg() - pos;
		char *b2 = new char[size+1];
		memset(b2, 0, size+1);
		ifs.seekg(pos, ios_base::beg);
		ifs.read(b2, size);
		
		ofs.write(b2, size);
	}
	
	ifs.close();
	ofs.close();
	
	return found;
}

inline unsigned char chToHex(char c)
{
	if ( '0' <= c && c <= '9' )
	{
		return c - 0x30;
	}
	else
	{
		return c - 0x41 + 10;
	}
}


void stringToLibId(const string& s, unsigned char* id)
{
	istringstream iss;
	iss.str(s);
	for ( unsigned i = 0; i < 8; ++i )
	{ 
		char c1, c2;
		iss >> setbase(16) >> c1 >> c2;
		c1 = chToHex(toupper(c1));
		c2 = chToHex(toupper(c2));
	
		id[i] = ( ((c1 << 4) ^ c2));
	}
}

bool patchFile(const string& path, const string& oldId, const string& newId)
{
	unsigned char oldLibraryId[8], newLibraryId[8];
	stringToLibId(oldId, oldLibraryId);
	stringToLibId(newId, newLibraryId);
	
	return patchFile(path, path+".patched", oldLibraryId, newLibraryId);
}


int main (int argc, char * const argv[]) {
	
	if ( argc != 4 )
	{
		cerr << "Usage: " << argv[0] << " /path/to/iTunes/Library/File HEX_STRING_OLD HEX_STRING_NEW " << endl;
		return 1;
	}
	
	string pathName = argv[1];
	string oldLibId = argv[2];
	string newLibId = argv[3];
	
	patchFile(pathName, oldLibId, newLibId);
	

	return 0;
}
